/*
  * This file is part of WhereYouGo.
  *
  * WhereYouGo is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * WhereYouGo is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with WhereYouGo.  If not, see <http://www.gnu.org/licenses/>.
  *
  * Copyright (C) 2012 Menion <whereyougo@asamm.cz>
  */ 

package menion.android.whereyougo.gui.extension;

import java.io.File;
import java.io.InputStream;
import java.io.StringReader;

import menion.android.whereyougo.R;
import menion.android.whereyougo.hardware.location.LocationState;
import menion.android.whereyougo.settings.Loc;
import menion.android.whereyougo.settings.Settings;
import menion.android.whereyougo.utils.A;
import menion.android.whereyougo.utils.FileSystem;
import menion.android.whereyougo.utils.Logger;
import menion.android.whereyougo.utils.Utils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import android.Manifest;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Debug;
import android.os.StatFs;
import android.view.KeyEvent;

public abstract class CustomMain extends CustomActivity {

	private static final String TAG = "CustomMain";


	// create directories during startup
	protected static String[] DIRS = new String[] {
		FileSystem.CACHE
	};
	
	private static boolean callSecondInit;
	private static boolean callRegisterOnly;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        A.registerMain(this);

        callSecondInit = false;
        callRegisterOnly = false;
        if (A.getApp() == null) { // first app run
//Logger.w(TAG, "onCreate() - init new");
        	A.registerApp((MainApplication) getApplication());

        	// not test some things
            if (testFileSystem() && testFreeSpace()) {
    	        // set last known location
    	        if (Utils.isPermissionAllowed(Manifest.permission.ACCESS_FINE_LOCATION) &&
    	        		Settings.getPrefBoolean(Settings.KEY_B_START_GPS_AUTOMATICALLY,
    	        		Settings.DEFAULT_START_GPS_AUTOMATICALLY)) {
    	        	LocationState.setGpsOn(CustomMain.this);
    	        } else {
    	        	LocationState.setGpsOff(CustomMain.this);
    	        }

        		eventFirstInit();
        		Settings.setScreenBasic(this);
        		eventCreateLayout();
        		callSecondInit = true;
            } else {
            	// do nothing, just close APP
            }
        } else {
//Logger.w(TAG, "onCreate() - only register");
        	Settings.setScreenBasic(this);
        	eventCreateLayout();
        	callRegisterOnly = true;
        }
    }
    
    public void onResumeExtra() {
    	if (callSecondInit) {
    		callSecondInit = false;
    		eventSecondInit();
    	}
    	if (callRegisterOnly) {
    		callRegisterOnly = false;
    		eventRegisterOnly();
    	}
    }

    private boolean testFileSystem() {
    	if (DIRS == null || DIRS.length == 0)
    		return true;
    	
   		if (FileSystem.createRoot(MainApplication.APP_NAME)) {
//Logger.w(TAG, "FileSystem succesfully created!");
    	} else {
//Logger.w(TAG, "FileSystem cannot be created!");
			UtilsGUI.showDialogError(CustomMain.this,
					R.string.filesystem_cannot_create_root,
					new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					showDialogFinish(FINISH_EXIT_FORCE);
				}
			});
			return false;
    	}
    	
    	// fileSystem created successfully
    	for (int i = 0; i < DIRS.length; i++) {
    		(new File(FileSystem.ROOT + DIRS[i])).mkdirs();
    	}
        return true;
    }
    
    private boolean testFreeSpace() {
    	if (DIRS == null || DIRS.length == 0)
    		return true;
    	
    	// check disk space (at least 5MB)
    	StatFs stat = new StatFs(FileSystem.ROOT);
    	long bytesFree = (long) stat.getBlockSize() *(long) stat.getAvailableBlocks();
    	long megFree = bytesFree / 1048576;
//Logger.d(TAG, "megAvailable:" + megAvail + ", free:" + megFree);
    	if (megFree > 0 && megFree < 5) {
    		UtilsGUI.showDialogError(CustomMain.this,
    				getString(R.string.not_enough_disk_space_x, FileSystem.ROOT, megFree + "MB"),
    				new DialogInterface.OnClickListener() {

    			@Override
    			public void onClick(DialogInterface dialog, int which) {
    				showDialogFinish(FINISH_EXIT_FORCE);
    			}
    		});
    		return false;
    	}
    	return true;
    }
    
    @Override
    public void onDestroy() {
		if (finish) {
			// stop debug if any forgotten
			Debug.stopMethodTracing();
			// remember value before A.getApp() exist
			boolean clearPackageAllowed = Utils.isPermissionAllowed(Manifest.permission.KILL_BACKGROUND_PROCESSES); 

			// call individual app close
			eventDestroyApp();

			// disable highlight
			Settings.disableWakeLock();
			// save last known location
			Settings.setLastKnownLocation();
			// disable GPS modul
			LocationState.destroy(CustomMain.this);

			// destroy static references
			A.destroy();
			// call native destroy
			super.onDestroy();

			// remove app from memory			
			if (clearPackageAllowed) {
				clearPackageFromMemory(); // XXX not work on 2.2 and higher!!!
			}
		} else {
			super.onDestroy();
		}
    }
    
    public static final int FINISH_NONE = -1;
    public static final int FINISH_EXIT = 0;
    public static final int FINISH_EXIT_FORCE = 1;
    public static final int FINISH_RESTART = 2;
    public static final int FINISH_RESTART_FORCE = 3;
    public static final int FINISH_RESTART_FACTORY_RESET = 4;
    public static final int FINISH_REINSTALL = 5;
    
    private boolean finish = false;
    protected int finishType = FINISH_NONE;
    
    public void showDialogFinish(final int typeOfFinish) {
//Logger.d(TAG, "showFinishDialog(" + typeOfFinish + ")");
    	if (typeOfFinish == FINISH_NONE)
    		return;
    	
    	this.finishType = typeOfFinish;
    	
    	runOnUiThread(new Runnable() { 
			public void run() {
				String title = Loc.get(R.string.question);
		    	String message = "";
		    	boolean cancelable = (
		    			finishType == FINISH_RESTART_FORCE ||
		    			finishType == FINISH_RESTART_FACTORY_RESET ||
		    			finishType == FINISH_REINSTALL ||
		    			finishType == FINISH_EXIT_FORCE) ? false : true; 
		    	switch (finishType) {
		    	case FINISH_EXIT:
		    		message = Loc.get(R.string.do_you_really_want_to_exit);
		    		break;
		    	case FINISH_EXIT_FORCE:
		    		title = Loc.get(R.string.info);
		    		message = Loc.get(R.string.you_have_to_exit_app_force);
		    		break;
				case FINISH_RESTART:
					message = Loc.get(R.string.you_have_to_restart_app_recommended);
					break;
				case FINISH_RESTART_FORCE:
					title = Loc.get(R.string.info);
					message = Loc.get(R.string.you_have_to_restart_app_force);
					break;
				case FINISH_RESTART_FACTORY_RESET:
					title = Loc.get(R.string.info);
					message = Loc.get(R.string.you_have_to_restart_app_force);
					break;
				case FINISH_REINSTALL:
					title = Loc.get(R.string.info);
					message = Loc.get(R.string.new_version_will_be_installed);
					break;
				}
		    	
		    	AlertDialog.Builder b = new AlertDialog.Builder(CustomMain.this);
		    	b.setCancelable(cancelable);
		    	b.setTitle(title);
		    	b.setIcon(R.drawable.ic_question_alt);
		    	b.setMessage(message);
		    	b.setPositiveButton(R.string.ok, 
		    			new DialogInterface.OnClickListener() {
					
		    		@Override
		    		public void onClick(DialogInterface dialog, int which) {
		    			if (finishType == FINISH_EXIT || finishType == FINISH_EXIT_FORCE) {
		    				finish = true;
		    				CustomMain.this.finish();
		    			} else if (finishType == FINISH_RESTART || finishType == FINISH_RESTART_FORCE ||
		    					finishType == FINISH_RESTART_FACTORY_RESET) {
				    		// Setup one-short alarm to restart my application in 3 seconds - TODO need use another context
//				    		AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//				    		Intent intent = new Intent(APP_INTENT_MAIN);
//				    		PendingIntent pi = PendingIntent.getBroadcast(CustomMain.this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
//				    		alarmMgr.set(AlarmManager.ELAPSED_REALTIME, System.currentTimeMillis() + 3000, pi); 
		    				finish = true;
		    				CustomMain.this.finish();
		    			} else if (finishType == FINISH_REINSTALL) {
		    				Intent intent = new Intent();
		    				intent.setAction(android.content.Intent.ACTION_VIEW);
		    				intent.setDataAndType(Uri.fromFile(new File(FileSystem.ROOT + "smartmaps.apk")), "application/vnd.android.package-archive");
		    				
		    				startActivity(intent); 
		    				showDialogFinish(FINISH_EXIT_FORCE);
		    			}
		    		}
		    	});
		    	if (cancelable) {
		    		b.setNegativeButton(R.string.cancel, null);
		    	}
		    	b.show();
		    }
		});
    }
    
    public void finishForceSilent() {
    	finish = true;
		CustomMain.this.finish();
    }
    
    private void clearPackageFromMemory() {
    	new Thread(new Runnable() {
			public void run() {
				try {
					ActivityManager aM = (ActivityManager) getApplicationContext().getSystemService(ACTIVITY_SERVICE);

					Thread.sleep(1250);
					aM.killBackgroundProcesses(getPackageName());
				} catch (Exception e) {
					Logger.e(TAG, "clearPackageFromMemory()", e);
				}
			}
		}).start();
    }
    
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
//Logger.d(TAG, "dispatchKeyEvent(" + event.getAction() + ", " + event.getKeyCode() + ")");
    	if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    		switch (getCloseValue()) {
			case CLOSE_DESTROY_APP_NO_DIALOG:
				finish = true;
    			CustomMain.this.finish();
    			return true;
			case CLOSE_DESTROY_APP_DIALOG_NO_TEXT:
				showDialogFinish(FINISH_EXIT);
    			return true;
			case CLOSE_DESTROY_APP_DIALOG_ADDITIONAL_TEXT:
				showDialogFinish(FINISH_EXIT);
    			return true;
			case CLOSE_HIDE_APP:
				// no action
				break;
			}
    	}
    	
    	return super.dispatchKeyEvent(event);
    }
    
    public static String loadAssetString(String name) {
    	InputStream is = null;
    	try {
			is = A.getMain().getAssets().open(name);
			int size = is.available();

			byte[] buffer = new byte[size];
			is.read(buffer);
			is.close();

			return new String(buffer);
		} catch (Exception e) {
			Logger.e(TAG, "loadAssetString(" + name + ")", e);
			return "";
		} finally {
			Utils.closeStream(is);
		}
    }
    
    public static String getNewsFromTo(int lastVersion, int actualVersion) {
//Logger.d(TAG, "getNewsFromTo(" + lastVersion + ", " + actualVersion + "), file:" + 
//		"news_" + (Const.isPro() ? "pro" : "free") + ".xml");
   		String versionInfo = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>";
      	String data = CustomMain.loadAssetString("news.xml");
      	if (data == null || data.length() == 0)
      		data = CustomMain.loadAssetString("news.xml");
    	if (data != null && data.length() > 0) {
            XmlPullParser parser;
            try {
            	XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            	parser = factory.newPullParser();
            	parser.setInput(new StringReader(data));

            	int event;
                String tagName;

                boolean correct = false;
                while (true) {
                    event = parser.nextToken();
                    if (event == XmlPullParser.START_TAG) {
                        tagName = parser.getName();
                        if (tagName.equalsIgnoreCase("update")) {
                        	String name = parser.getAttributeValue(null, "name");
                        	int id = Utils.parseInt(parser.getAttributeValue(null, "id"));
                        	if (id > lastVersion && id <= actualVersion) {
                        		correct = true;
                        		versionInfo += ("<h4>" + name + "</h4><ul>");
                        	} else {
                        		correct = false;
                        	}
                        } else if (tagName.equalsIgnoreCase("li")) {
                        	if (correct) {
                        		versionInfo += ("<li>" + parser.nextText() + "</li>");
                        	}
                        }
                    } else if (event == XmlPullParser.END_TAG) {
                    	tagName = parser.getName();
                        if (tagName.equalsIgnoreCase("update")) {
                        	if (correct) {
                        		correct = false;
                        		versionInfo += "</ul>";
                        	}
                        } else if (tagName.equals("document")) {
                        	break;
                        }
                    }
                }
            } catch (Exception e) {
            	Logger.e(TAG, "getNews()", e);
            }
    	}
   		
		versionInfo += "</body></html>";
    	return versionInfo;
    }
    
	/**
	 * This is called only once after application start. It's
	 * called in onCreate method before layout is placed
	 */
    protected abstract void eventFirstInit();
    
    /**
     * Method that create layout for actual activity. This is
     * called everytime, onCreate method is called
     */
    protected abstract void eventCreateLayout();
    
	/**
	 * This is called only once after application start. It's
	 * called in onResume method
	 */
    protected abstract void eventSecondInit();
    
	/**
	 * This is called everytime except first run. It's
	 * called in onResume method
	 */
    protected abstract void eventRegisterOnly();
    
    /**
     * This is called only when application really need to be destroyed,
     * so in this method is suggested to clear all variables
     */
    protected abstract void eventDestroyApp();
    
    public static final int CLOSE_DESTROY_APP_NO_DIALOG = 0;
    public static final int CLOSE_DESTROY_APP_DIALOG_NO_TEXT = 1;
    public static final int CLOSE_DESTROY_APP_DIALOG_ADDITIONAL_TEXT = 2;
    public static final int CLOSE_HIDE_APP = 3;
    
    protected abstract int getCloseValue();
    
    protected abstract String getCloseAdditionalText();
}

